package org.offsync.async

import org.offsync.util.Window
import org.offsync.rich._
import java.nio.charset.Charset

trait AsyncOutputStream {
  private val writeWindow = new Window[Byte](8)
  
  def writeSome(window: Window[Byte])(task: (() => Int) => Unit): Unit

  def writeFully(window: Window[Byte])(task: (() => Int) => Unit): Unit
  
  def writeShort(value: Short) = new (((() => Int) => Unit) => Unit) {
    def apply(writeShortCompletion: (() => Int) => Unit): Unit = {
      val intWindow = writeWindow.until(2)
      
      intWindow(0) = ((value >>> 8) & 0xff).asInstanceOf[Byte]
      intWindow(1) = ((value >>> 0) & 0xff).asInstanceOf[Byte]
        
      writeFully(intWindow) { readCompletion =>
        val bytesWritten = try {
          readCompletion()
        } catch {
          case e => {
            writeShortCompletion { () => throw e.wrap() }
            return
          }
        }
        
        writeShortCompletion { () => bytesWritten }
      }
    }
  }
  
  def writeInt(value: Int) = new (((() => Int) => Unit) => Unit) {
    def apply(writeIntCompletion: (() => Int) => Unit): Unit = {
      val intWindow = writeWindow.until(4)
      
      intWindow(0) = ((value >>> 24) & 0xff).asInstanceOf[Byte]
      intWindow(1) = ((value >>> 16) & 0xff).asInstanceOf[Byte]
      intWindow(2) = ((value >>>  8) & 0xff).asInstanceOf[Byte]
      intWindow(3) = ((value >>>  0) & 0xff).asInstanceOf[Byte]

      writeFully(intWindow) { writeCompletion =>
        val bytesWritten = try {
          writeCompletion()
        } catch {
          case e => {
            writeIntCompletion { () => throw e.wrap() }
            return
          }
        }
        
        writeIntCompletion { () => 4 }
      }
    }
  }
  
  def writeLong(value: Long) = new (((() => Int) => Unit) => Unit) {
    def apply(writeLongCompletion: (() => Int) => Unit): Unit = {
      val longWindow = writeWindow.until(8)
      
      longWindow(0) = ((value >>> 56) & 0xff).asInstanceOf[Byte]
      longWindow(1) = ((value >>> 48) & 0xff).asInstanceOf[Byte]
      longWindow(2) = ((value >>> 40) & 0xff).asInstanceOf[Byte]
      longWindow(3) = ((value >>> 32) & 0xff).asInstanceOf[Byte]
      longWindow(4) = ((value >>> 24) & 0xff).asInstanceOf[Byte]
      longWindow(5) = ((value >>> 16) & 0xff).asInstanceOf[Byte]
      longWindow(6) = ((value >>>  8) & 0xff).asInstanceOf[Byte]
      longWindow(7) = ((value >>>  0) & 0xff).asInstanceOf[Byte]

      writeFully(longWindow) { writeCompletion =>
        val bytesWritten = try {
          writeCompletion()
        } catch {
          case e => {
            writeLongCompletion { () => throw e.wrap() }
            return
          }
        }
        
        writeLongCompletion { () => 8 }
      }
    }
  }

  def writeCharacters(value: String) = new (((() => Int) => Unit) => Unit) {
    def apply(writeLongCompletion: (() => Int) => Unit): Unit = {
      val window = value.window(Default.charset)
      writeFully(window) { writeCompletion =>
        val bytesWritten = try {
          writeCompletion()
        } catch {
          case e => {
            writeLongCompletion { () => throw e.wrap() }
            return
          }
        }
        
        writeLongCompletion { () => window.length }
      }
    }
  }
}
